package com.ipan.poi.easyexcel.ehcache2;

import java.net.URL;
import java.util.ArrayList;

import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.excel.cache.ReadCache;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.util.ListUtils;
import com.ipan.kits.id.IdUtil;
import com.ipan.kits.io.ResourceUtil;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

/**
 * 支持EhCache2.x的版本
 * 
 * 配置文件，见：
 * ehcache-easyexcel-disk.xml
 * <diskStore path="java.io.tmpdir"/>
 * <defaultCache 
 * 		maxEntriesLocalHeap="1"
 * 		maxBytesLocalDisk="5G"
 * 		eternal="false"
 * 		timeToIdleSeconds="7200"
 * 		timeToLiveSeconds="43200"
 * 		diskSpoolBufferSizeMB="20"
 * 		memoryStoreEvictionPolicy="LRU"
 * 		overflowToDisk="true"
 * 		diskPersistent="false"
 * 		statistics="false">
 * 		</defaultCache>
	
 * ehcache-easyexcel-mem.xml
 * <defaultCache
 * 		maxBytesLocalHeap="30M"
 * 		eternal="false"
 * 		timeToIdleSeconds="7200"
 * 		timeToLiveSeconds="43200"
 * 		memoryStoreEvictionPolicy="FIFO"
 * 		overflowToDisk="false"
 * 		diskPersistent="false"
 * 		statistics="false">
 * 		</defaultCache>
 * 
 * @author iPan
 * @date 2022-01-21
 */
public class EhCache2 implements ReadCache {
	private static Logger LOG = LoggerFactory.getLogger(EhCache2.class);
	public static final int BATCH_COUNT = 100;
    /**
     * 活动缓存的位置
     */
    private int activeIndex = 0;
    public static final int DEBUG_CACHE_MISS_SIZE = 1000;
    public static final int DEBUG_WRITE_SIZE = 100 * 10000;
    private ArrayList<String> dataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    private static final CacheManager FILE_CACHE_MANAGER;
    private static final CacheManager ACTIVE_CACHE_MANAGER;
    /** 磁盘缓存 */
    private net.sf.ehcache.Ehcache fileCache;
    /** 内存活动缓存 */
    private net.sf.ehcache.Ehcache activeCache;
    /** 当前读取文件对应的缓存别名 */
    private String cacheAlias;
    /**
     * Count the number of cache misses
     */
    private int cacheMiss = 0;
//    /** 内存空间大小（单位M） */
//    private int maxCacheActivateSize = 20; // 配在配置文件内了
    
    static {
    	URL fileUrl = ResourceUtil.asUrl("ehcache-easyexcel-disk.xml");
    	URL activeUrl = ResourceUtil.asUrl("ehcache-easyexcel-mem.xml");
    	FILE_CACHE_MANAGER = CacheManager.create(fileUrl);
    	ACTIVE_CACHE_MANAGER = CacheManager.create(activeUrl);
    }

    public EhCache2() {
//    	this.maxCacheActivateSize = maxCacheActivateSize;
    }
    
    static Cache getOrAddCache(CacheManager cacheManager, String cacheName) {
		Cache cache = cacheManager.getCache(cacheName);
		if (cache == null) {
			synchronized(EhCache2.class) {
				cache = cacheManager.getCache(cacheName);
				if (cache == null) {
					cacheManager.addCacheIfAbsent(cacheName);
					cache = cacheManager.getCache(cacheName);
					LOG.debug("Cache [" + cacheName + "] started.");
				}
			}
		}
		return cache;
	}

    @Override
    public void init(AnalysisContext analysisContext) {
        cacheAlias = IdUtil.fastUUID().toString();
        fileCache = FILE_CACHE_MANAGER.addCacheIfAbsent(cacheAlias);
        activeCache = ACTIVE_CACHE_MANAGER.addCacheIfAbsent(cacheAlias);
    }

    @Override
    public void put(String value) {
        dataList.add(value);
        if (dataList.size() >= BATCH_COUNT) {
            put(fileCache, activeIndex, dataList);
            activeIndex++;
            dataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
        if (LOG.isDebugEnabled()) {
            int alreadyPut = activeIndex * BATCH_COUNT + dataList.size();
            if (alreadyPut % DEBUG_WRITE_SIZE == 0) {
                LOG.debug("Already put :{}", alreadyPut);
            }
        }
    }

    @Override
    public String get(Integer key) {
        if (key == null || key < 0) {
            return null;
        }
        int route = key / BATCH_COUNT;
        Element dataElement = activeCache.get(route);
        ArrayList<String> dataList = (dataElement != null) ? (ArrayList<String>) dataElement.getObjectValue() : null;
        if (dataList == null) {
            dataList = get(fileCache, route);
            put(activeCache, route, dataList);
            if (LOG.isDebugEnabled()) {
                if (cacheMiss++ % DEBUG_CACHE_MISS_SIZE == 0) {
                    LOG.debug("Cache misses count:{}", cacheMiss);
                }
            }
        }
        return dataList.get(key % BATCH_COUNT);
    }

    @Override
    public void putFinished() {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }
        put(fileCache, activeIndex, dataList);
    }

    @Override
    public void destroy() {
        FILE_CACHE_MANAGER.removeCache(cacheAlias);
        ACTIVE_CACHE_MANAGER.removeCache(cacheAlias);
    }
    
    public static void put(net.sf.ehcache.Ehcache cache, Object key, Object value) {
		cache.put(new Element(key, value));
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T get(net.sf.ehcache.Ehcache cache, Object key) {
		Element element = cache.get(key);
		return element != null ? (T)element.getObjectValue() : null;
	}
	
}
